package com.smart.tool.core;

import com.google.common.collect.Lists;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author xiaoqiang
 * @Version FnTool.java, v 0.1 2025年09月16日 00:02 xiaoqiang
 * @Description: TODO
 */
public class FnTool {


    /**
     * 获取类中的方法，转换位BiFunction
     *
     * @param clazz     类
     * @param fieldName 字段名
     * @param converter 转换器
     * @param <T>       类泛型
     * @param <R>       返回值泛型
     * @return 字段值
     */
    public static <T, R> BiFunction<T, Object, R> getterWithConvert(
            Class<T> clazz, String fieldName, Function<Object, R> converter) {
        try {
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            return (obj, unused) -> {
                try {
                    return converter.apply(field.get(obj));
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            };
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取类中的方法，转换位Function
     *
     * @param clazz     类
     * @param fieldName 字段名
     * @param <T>       类泛型
     * @param <R>       返回值泛型
     * @return 字段值
     */
    public static <T, R> Function<T, R> getFunction(Class<T> clazz, String fieldName) {
        try {
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            return obj -> {
                try {
                    return (R) field.get(obj);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            };
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取类中的方法，转换位Function
     *
     * @param func       func
     * @param defaultVal 字段名
     * @return 字段值
     */
    public static <T, R> Function<T, R> safe(Function<T, R> func, R defaultVal) {
        if (func == null) {
            return t -> defaultVal;
        }
        return t -> {
            try {
                return func.apply(t);
            } catch (Exception e) {
                return defaultVal;
            }
        };
    }

    /**
     * 将BiFunction转为Function（柯里化）
     */
    public static <T, U, R> Function<T, Function<U, R>> curry(BiFunction<T, U, R> biFunction) {
        return t -> u -> biFunction.apply(t, u);
    }

    /**
     * 将Function转为BiFunction（反柯里化）
     */
    public static <T, U, R> BiFunction<T, U, R> uncurry(Function<T, Function<U, R>> function) {
        return (t, u) -> function.apply(t).apply(u);
    }

    /**
     * 安全Predicate，异常时返回false
     */
    public static <T> Predicate<T> safe(Predicate<T> predicate) {
        return t -> {
            try {
                return predicate.test(t);
            } catch (Exception e) {
                return false;
            }
        };
    }

    /**
     * 安全Predicate，异常时返回false
     */
    public static <T> Predicate<T> safeException(Predicate<T> predicate) {
        return t -> {
            try {
                return predicate.test(t);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }


    /**
     * 安全Supplier，异常时返回默认值
     */
    public static <T> Supplier<T> safeException(Supplier<T> supplier, T defaultValue) {
        return () -> {
            try {
                return supplier.get();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }

    /**
     * 数字安全转换
     */
    public static Function<String, Integer> toInt(Integer defaultValue) {
        return safe(s -> Integer.parseInt(s), defaultValue);
    }

    public static Function<String, Long> toLong(Long defaultValue) {
        return safe(s -> Long.parseLong(s), defaultValue);
    }

    /**
     * 安全Supplier，异常时返回默认值
     */
    public static <T> Supplier<T> safe(Supplier<T> supplier, T defaultValue) {
        return () -> {
            try {
                return supplier.get();
            } catch (Exception e) {
                return defaultValue;
            }
        };
    }

    /**
     * 安全执行函数，异常时返回Optional.empty()
     */
    public static <T, R> Function<T, Optional<R>> safeOpt(Function<T, R> function) {
        return t -> {
            try {
                return Optional.ofNullable(function.apply(t));
            } catch (Exception e) {
                return Optional.empty();
            }
        };
    }

    /**
     * 获取类中的方法，转换位Function
     *
     * @param func       func
     * @param defaultVal 字段名
     * @return 字段值
     */
    public static <T, R> Function<T, R> safeException(Function<T, R> func, R defaultVal) {
        if (func == null) {
            return t -> defaultVal;
        }
        return t -> {
            try {
                return func.apply(t);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }

    /**
     * 最大 / 最小
     */
    public static <T, U extends Comparable<? super U>> Function<List<T>, Optional<T>> maxBy(Function<? super T, ? extends U> key) {
        return list -> list.stream().max(Comparator.comparing(key));
    }

    public static <T, U extends Comparable<? super U>> Function<List<T>, Optional<T>> minBy(Function<? super T, ? extends U> key) {
        return list -> list.stream().min(Comparator.comparing(key));
    }

    /**
     * 切分批量处理
     *
     * @param size        分割数量
     * @param batchCaller 批量处理
     * @param <T>         泛型
     * @param <R>         泛型
     * @return 批量处理结果
     */
    public static <T, R> Function<List<T>, List<R>> batch(int size,
                                                          Function<List<T>, List<R>> batchCaller) {
        return list -> Lists.partition(list, size)
                .stream()
                .flatMap(chunk -> batchCaller.apply(chunk).stream())
                .collect(Collectors.toList());
    }

    /**
     * 内存分页
     *
     * @param <T> 泛型
     * @return 分页
     */
    public static <T> BiFunction<List<T>, Pageable, List<T>> page(Comparator<T> comparator, Predicate<T>... predicates) {

        return (list, pageable) -> {
            Stream<T> stream = list.stream();
            if (comparator != null) {
                stream = stream.sorted(comparator);
            }
            if (predicates != null && predicates.length > 0) {
                for (Predicate<T> predicate : predicates) {
                    stream = stream.filter(predicate);
                }

            }
            return stream.skip(pageable.getOffset())
                    .limit(pageable.getPageSize())
                    .collect(Collectors.toList());
        };
    }

    public static class Pageable {
        private int page;
        private int size;

        private Pageable(int page, int size) {
            this.page = page;
            this.size = size;
        }

        public static Pageable of(int page, int size) {
            if (page < 1) {
                page = 1;
            }
            if (size < 1) {
                size = 1;
            }
            return new Pageable(page, size);
        }

        public int getPageNumber() {
            return page;
        }

        public int getPageSize() {
            return size;
        }

        public int getOffset() {
            return (page - 1) * size;
        }
    }

    public static void main(String[] args) {
        Function<String, Integer> safeParseInt = FnTool.safe(s -> Integer.parseInt(s), 0);
        System.out.println(safeParseInt.apply("123")); // 123
        System.out.println(safeParseInt.apply("abc")); // 0

        // 示例3：柯里化
        BiFunction<String, String, String> concat = (a, b) -> a + b;
        Function<String, Function<String, String>> curried = FnTool.curry(concat);
        Function<String, String> greet = curried.apply("Hello, ");
        System.out.println(greet.apply("World!")); // Hello, World!


        List<String> list = Arrays.asList("1", "2", "3", "4", "5");
        Function<List<String>, List<String>> batch = FnTool.batch(2, list1 -> {
            System.out.println(list1);
            return list1;
            //return list1.stream().map(s -> Integer.parseInt(s)).collect(Collectors.toList());
        });
        batch.apply(list);

        Pageable pageable = Pageable.of(1, 3);
        List<String> list1 = Arrays.asList("1", "2", "3", "4", "5");
        List<String> page = FnTool.<String>page(null).apply(list1, pageable);
        System.out.println(page);
    }


}
